package personal.lijun.skeleton.web.access;

import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import personal.lijun.skeleton.web.feign.CalendarServiceFeign;
import personal.lijun.skeleton.web.feign.CalendarUser;
import personal.lijun.skeleton.web.feign.Event;

import java.io.Serializable;
import java.util.List;

/**
 * @Auther: lijun@sstcsoft.com
 * @Date: 2019/9/18 17:30
 */
public class CalendarPermissionEvaluator  implements PermissionEvaluator {

    private final CalendarServiceFeign eventDao;

    public CalendarPermissionEvaluator(CalendarServiceFeign eventDao) {
        this.eventDao = eventDao;
    }

    /**
     * This method encapsulates our logic of determining if the current user has access to an {@link Event}. If we were
     * securing other domain objects, the logic could be added to here as well.
     *
     * @param authentication The current Spring Security {@link Authentication} which is not specified in the SpEL since it is automatically passed in by Spring Security.
     * @param targetDomainObject the object that we are determining if access is granted to. In our case, this is an {@link Event} object.
     * @param permission The type of permission for the {@link Event}. This is typically a String (i.e. "read", "write", etc).
     */
    @Override
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
        Event event = null;
        if(targetDomainObject instanceof List){
            for(Object obj : (List)targetDomainObject){
                if(!(obj instanceof Event))
                    return false;
                else{
                    event = (Event)obj;
                    if(!hasPermission(authentication, event, permission)){
                        return false;
                    }
                }
            }
            return true;
        }

        else if(targetDomainObject instanceof Event) {
            return hasPermission(authentication, (Event) targetDomainObject, permission);
        }

        return targetDomainObject == null;
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,
                                 Object permission) {
        if(!Event.class.getName().equals(targetType)) {
            throw new IllegalArgumentException("targetType is not supported. Got "+targetType);
        }
        if(!(targetId instanceof Integer)) {
            throw new IllegalArgumentException("targetId type is not supported. Got "+targetType);
        }
        Event event = eventDao.getEvent((Long) targetId);
        return hasPermission(authentication, event, permission);
    }

    private boolean hasPermission(Authentication authentication, Event event, Object permission) {
        if(event == null) {
            return true;
        }
        String currentUserEmail = authentication.getName();
        CalendarUser owner = eventDao.getUser(event.getOwner());
        String ownerEmail = extractEmail(owner);
        if("write".equals(permission)) {
            return currentUserEmail.equals(ownerEmail);
        } else if("read".equals(permission)) {
            CalendarUser attendee = eventDao.getUser(event.getAttendee());
            String attendeeEmail = extractEmail(attendee);
            return currentUserEmail.equals(attendeeEmail) || currentUserEmail.equals(ownerEmail);
        }
        throw new IllegalArgumentException("permission "+permission+" is not supported.");
    }

    private String extractEmail(CalendarUser user) {
        if(user == null) {
            return null;
        }
        return user.getEmail();
    }
}
